package com.zlc.workflow.core.instance.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.zlc.common.core.process.model.entity.StoneEventConfig;
import com.zlc.common.core.process.model.entity.StoneNodeApproveUserConfig;
import com.zlc.common.core.process.model.vo.*;
import com.zlc.common.core.process.service.*;
import com.zlc.common.core.user.model.entity.MayanPlatUser;
import com.zlc.common.core.user.model.vo.MayanPlatUserVo;
import com.zlc.common.core.user.service.MayanPlatUserService;
import com.zlc.common.util.HttpUtils;
import com.zlc.common.util.PlatUtils;
import com.zlc.workflow.anno.WorkflowCallback;
import com.zlc.workflow.anno.WorkflowCallbackFunction;
import com.zlc.workflow.constant.EventTimeEnum;
import com.zlc.workflow.constant.ProcessLogTypeEnum;
import com.zlc.workflow.core.instance.model.EventParam;
import com.zlc.workflow.core.instance.service.EventService;
import com.zlc.workflow.core.instance.service.WorkflowService;
import com.zlc.workflow.flow.NodeUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;

import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static com.zlc.common.common.constant.WorkflowConstant.*;

@Slf4j
@Service
@Transactional(rollbackFor = Exception.class)
public class EventServiceImpl implements EventService {

    @Autowired
    private StoneEventConfigService stoneEventConfigService;
    @Autowired
    private StoneInterfaceConfigService stoneInterfaceConfigService;
    @Autowired
    private StoneProcessLogService stoneProcessLogService;
    @Autowired
    private StoneNodeApproveUserConfigService stoneNodeApproveUserConfigService;
    @Autowired
    private RabbitTemplate rabbitTemplate;
    @Autowired
    private MayanPlatUserService mayanPlatUserService;
    @Autowired
    private StoneTaskHistoryService stoneTaskHistoryService;
    @Autowired
    private WorkflowService workflowService;
    @Autowired
    private StoneProcessNodeService stoneProcessNodeService;
    @Autowired
    private List<WorkflowCallbackFunction> callbackFunctions;

    @Override
    public void execute(EventParam param) {
        String processId = param.getProcessId();
        String nodeId = param.getNodeId();
        String eventTime = param.getEventTime();
        if(ObjectUtils.isEmpty(eventTime) || ObjectUtils.isEmpty(processId)){
            return;
        }
        //事件配置
        if(EventTimeEnum.isProcessType(eventTime)){
            //如果是流程事件,则设置节点id为空
            nodeId = null;
        }
        List<StoneEventConfigVo> eventConfigVos = eventConfig(processId,nodeId,eventTime);
        if(ObjectUtils.isEmpty(eventConfigVos)){
            return;
        }
        for(StoneEventConfigVo eventConfigVo : eventConfigVos){
            String eventType = eventConfigVo.getEventType();
            if(EVENT_TYPE_AUTO_EXEC.equals(eventType)){
                //自动处理
                String interId = eventConfigVo.getInterId();
                callback(param,interId);
            } else if (EVENT_TYPE_READ_NODE.equals(eventType)) {
                //传阅
                readNode(param,eventConfigVo.getEventId());
            }

        }
    }

    private void readNode(EventParam param,String eventId){
        if(ObjectUtils.isEmpty(eventId)){
            return;
        }
        StoneProcessNodeVo nodeConfig = stoneProcessNodeService.getByNodeId(param.getProcessId(), param.getNodeId());
        //审批人数据
        Map<String, Object> formData = param.getFormData();
        List<MayanPlatUserVo> userVos = workflowService.getApprovalUserByEventId(param.getProcessInstanceId(),eventId,formData);
        if(!ObjectUtils.isEmpty(userVos)) {
            for (MayanPlatUserVo userVo : userVos) {
                StoneTaskHistoryVo taskVo = new StoneTaskHistoryVo();
                taskVo.setProcessInstanceId(param.getProcessInstanceId());
                taskVo.setTaskId(PlatUtils.uuid());
                taskVo.setActorId(userVo.getUserAccount());
                taskVo.setActorName(userVo.getUsername());
                taskVo.setExt("{}");
                taskVo.setStatus(TASK_STATUS_COMPLETE);
                taskVo.setOperateType(TASK_OPERATE_TYPE_NODE);
                taskVo.setNodeId(param.getNodeId());
                taskVo.setNodeName(param.getNodeName());
                taskVo.setNodeType(param.getNodeType());
                taskVo.setProcessId(param.getProcessId());
                taskVo.setReceiveTime(new Date());
                taskVo.setExecuteTime(new Date());
                if(!ObjectUtils.isEmpty(nodeConfig)){
                    taskVo.setUrl(nodeConfig.getExecForm());
                }
                stoneTaskHistoryService.saveVo(taskVo);
            }
            saveNodeLog(param, userVos);
        }
    }

    private void callback(EventParam param,String interId){
        if(ObjectUtils.isEmpty(interId)){
            return;
        }
        StoneInterfaceConfigVo configVo = stoneInterfaceConfigService.getByInterId(interId);
        if(ObjectUtils.isEmpty(configVo)){
            return;
        }
        String interType = configVo.getInterType();
        if(INTER_TYPE_HTTP.equals(interType)){
            //http请求
            httpExecute(param,configVo);
        }else if(INTER_TYPE_MQ.equals(interType)){
            //mq请求
            mqExecute(param,configVo);
        }else if(INTER_TYPE_CODE.equals(interType)){
            //系统回调接口
            codeExecute(param,configVo);
        }
    }

    private void codeExecute(EventParam param,StoneInterfaceConfigVo configVo){
        String codeValue = configVo.getCodeValue();
        callbackFunctions.forEach(callbackFunction -> {
            WorkflowCallback annotation = callbackFunction.getClass().getAnnotation(WorkflowCallback.class);
            if(annotation != null && annotation.value().equals(codeValue)){
                callbackFunction.callback(param);
            }
        });
    }

    private void mqExecute(EventParam param,StoneInterfaceConfigVo configVo){
        String exchange = configVo.getExchange();
        String routingKey = configVo.getRoutingKey();
        if(ObjectUtils.isEmpty(routingKey)){
            //工作流mq必须要routingKey
            return;
        }
        PlatUtils.THREAD_POOL_EXECUTOR.execute(()->{
            log.info("开始执行回调接口："+configVo.getInterName());
            if(ObjectUtils.isEmpty(exchange)){
                rabbitTemplate.convertAndSend(routingKey,param);
            }else{
                rabbitTemplate.convertAndSend(exchange,routingKey,param);
            }
            //记录日志
            saveCallLog(param,configVo);
        });
    }

    private void httpExecute(EventParam param,StoneInterfaceConfigVo configVo){
        String url = configVo.getUrl();
        if(ObjectUtils.isEmpty(url)){
            return;
        }
        PlatUtils.THREAD_POOL_EXECUTOR.execute(()->{
            String method = configVo.getRequestMethod();
            if(ObjectUtils.isEmpty(method) || !Arrays.asList("POST","post","PUT","put","DELETE","delete").contains(method)){
                method = "POST";
            }
            log.info("开始执行回调接口："+configVo.getInterName());
            if("post".equalsIgnoreCase(method)){
                HttpUtils.postJson(url,param);
            } else if ("put".equalsIgnoreCase(method)) {
                HttpUtils.putJson(url,param);
            }else if("delete".equalsIgnoreCase(method)){
                HttpUtils.deleteJson(url,param);
            }
            //记录日志
            saveCallLog(param,configVo);
        });
    }

    private void saveNodeLog(EventParam param,List<MayanPlatUserVo> userVos){
        if(ObjectUtils.isEmpty(userVos)){
            return;
        }
        //记录日志
        StoneProcessLogVo logVo = new StoneProcessLogVo();
        logVo.setProcessInstanceId(param.getProcessInstanceId());
        logVo.setProcessId(param.getProcessId());
        logVo.setNodeId(param.getNodeId());
        logVo.setOperateUser(param.getActorId());
        logVo.setOperateUserName(param.getActorName());
        logVo.setTaskId(param.getTaskId());
        logVo.setOperateType(ProcessLogTypeEnum.EVENT_NODE.getType());
        String content = "";
        String eventTime = param.getEventTime();
        EventTimeEnum timeEnum = EventTimeEnum.getType(eventTime);
        if(!ObjectUtils.isEmpty(timeEnum)){
            content = timeEnum.getDesc()+"被执行,";
        }
        String accounts = userVos.stream().map(MayanPlatUserVo::getUserAccount).collect(Collectors.joining(";"));
        logVo.setLogContent(content+"添加备案传阅记录，备案给->"+accounts);
        stoneProcessLogService.saveVo(logVo);
    }

    private void saveCallLog(EventParam param,StoneInterfaceConfigVo configVo){
        //记录日志
        StoneProcessLogVo logVo = new StoneProcessLogVo();
        logVo.setProcessInstanceId(param.getProcessInstanceId());
        logVo.setProcessId(param.getProcessId());
        logVo.setNodeId(param.getNodeId());
        logVo.setOperateUser(param.getActorId());
        logVo.setTaskId(param.getTaskId());
        logVo.setOperateType(ProcessLogTypeEnum.EVENT_CALL.getType());

        String content = "";
        String eventTime = param.getEventTime();
        EventTimeEnum timeEnum = EventTimeEnum.getType(eventTime);
        if(!ObjectUtils.isEmpty(timeEnum)){
            content = timeEnum.getDesc()+"被执行,";
        }
        logVo.setLogContent(content+"执行回调接口："+configVo.getInterName()+" | "+configVo.getUrl()+" "+configVo.getExchange() + " "+configVo.getRoutingKey());
        stoneProcessLogService.saveVo(logVo);
    }

    private List<StoneEventConfigVo> eventConfig(String processId,String nodeId,String eventTime){
        LambdaQueryWrapper<StoneEventConfig> wrapper = stoneEventConfigService.wrapper()
                .eq(StoneEventConfig::getEventTime, eventTime)
                .eq(StoneEventConfig::getProcessId, processId);
        if(!ObjectUtils.isEmpty(nodeId)){
            wrapper.eq(StoneEventConfig::getNodeId,nodeId);
        }
        wrapper.orderByAsc(StoneEventConfig::getOrder);
        return stoneEventConfigService.queryVoList(wrapper);
    }
}
